home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC World Interactive 7
/
PC World Interactive 7.iso
/
program
/
ctutor.exe
/
TEXT
/
CHAP09.TXT
< prev
next >
Wrap
Text File
|
1994-05-15
|
31KB
|
638 lines
Chapter 9
STANDARD INPUT/OUTPUT
THE STDIO.H HEADER FILE
-----------------------------------------------------------------
Examine the file SIMPLEIO.C for our first look ================
at a file with standard I/O. Standard I/O SIMPLEIO.C
refers to the most usual places where data is ================
either read from, the keyboard, or written to,
the video monitor. Since they are used so much, they are used as
the default I/O devices and do not need to be named in the
Input/Output instructions. This will make more sense when we
actually start to use them so let's look at the file in front
of you.
The first thing you should take notice of is the second line of
the example file, the line with #include "stdio.h". This is very
much like the #define we have already studied, except that
instead of a simple substitution, an entire file is read in at
this point. The system will find the file named STDIO.H and read
its entire contents in, replacing this statement. Obviously
then, the file named STDIO.H must contain valid C source
statements that can be compiled as part of a program. You will
recall that we stated earlier that the preprocessor does textual
substitution. This particular file is composed of several
standard #defines to define some of the standard I/O operations.
The file is called a header file and you will find several
different header files on the source disks that came with your C
compiler. Each of the header files has a specific purpose and
any or all of them can be included in any program. Most header
files contain definitions of a few types, function prototypes for
the functions in its group, and some macros.
Your C compiler uses the double quote marks to indicate that the
search for the include file will begin in the current directory,
and if it not found there, the search will continue in the
include directory as set up in the environment for your compiler.
It also uses the "less than" and "greater than" signs to indicate
that the file search should begin in the directory specified in
the environment. Most of the programs in this tutorial have the
double quotes in the include statements. The next program uses
the "<" and ">" to illustrate the usage. Note that this will
result is a slightly faster (but probably unnoticeable)
compilation because the system will not bother to search the
current directory first. If you know the include file is not in
the current directory, it is best to use the "<" and ">" with the
filename.
As many includes can be used as necessary, and it is perfectly
all right for one header file to include one or more additional
header files. It is very common to include four or five header
files in a program.
Page 9-1
Chapter 9 - Standard Input/Output
INPUT/OUTPUT OPERATIONS IN C
-----------------------------------------------------------------
Actually the C programming language has no input or output
operations defined as part of the language, they must be user
defined. Since everybody does not want to reinvent his own input
and output operations, the compiler writers have done a lot of
this for us and supplied us with several input functions and
several output functions to aid in our program development. The
functions have become a standard, and you will find the same
functions available in nearly every compiler. In fact, the
industry standard of the C language definition has become the
book written by Kernigan and Ritchie, and they have included
these functions in their definition. Occasionally, when reading
literature about C, you will find an author refer to K & R. This
refers to the book, "The C Programming Language", written by
Kernigan and Ritchie. You would be advised to purchase a copy
for reference. As of this writing, a second edition of this book
is available and is definitely the preferred edition.
You should print out the file named STDIO.H and spend some time
studying it. There will be a lot that you will not understand
about it, but parts of it will look familiar. The name STDIO.H
is sort of cryptic for "standard input/output header", because
that is exactly what it is. It defines the standard input and
output functions in the form of #defines, macros, and prototypes
for the functions. Don't worry too much about the details of
this now. You can always return to this topic later for more
study if it interests you, but you will really have no need to
completely understand the STDIO.H file. You will have a
tremendous need to use it however, so these comments on its use
and purpose are necessary.
OTHER INCLUDE FILES
-----------------------------------------------------------------
When you begin writing larger programs and splitting them up into
separately compiled portions, you will have occasion to use some
definitions common to each of the portions. It would be to your
advantage to make a separate file containing the definitions and
use the #include to insert it into each of the files. If you
want to change any of the common statements, you will only need
to change one file and you will be assured of having all of the
common statements agree. This is getting a little ahead of
ourselves but you now have an idea how the #include directive
can be used.
BACK TO THE FILE NAMED SIMPLEIO.C
-----------------------------------------------------------------
Let's continue our tour of the file in question. The one
variable named c is defined and a message is printed out with the
familiar printf() function. We then find ourselves in a
continuous loop as long as the value of c is not equal to
Page 9-2
Chapter 9 - Standard Input/Output
capital X. If there is any question in your mind about the loop
control, you should review chapter 3 before continuing. The two
new functions within the loop are of paramount interest in this
program since they are the new functions. These are functions to
read a character from the keyboard and display it on the monitor
one character at a time.
The function getchar() reads a single character from the standard
input device, the keyboard being assumed because that is the
standard input device, and assigns it to the variable named c.
The next function putchar(), uses the standard output device, the
video monitor, and outputs the character contained in the
variable named c. The character is output at the current cursor
location and the cursor is advanced one space for the next
character. The system is therefore taking care of a lot of the
overhead for us. The loop continues reading and displaying
characters until we type a capital X which terminates the loop.
Compile and run this program for a few surprises. When you type
on the keyboard, you will notice that what you type is displayed
faithfully on the screen, and when you hit the return key, the
entire line is repeated. We only told it to output each
character once but it seems to be saving the characters up and
redisplaying them. A short explanation is in order.
DOS IS HELPING US OUT
-----------------------------------------------------------------
We need to understand a little bit about how DOS works to
understand what is happening here. When data is read from the
keyboard, under DOS control, the characters are stored in a
buffer until a carriage return is entered at which time the
entire string of characters is given to the program. When the
characters are being typed, however, the characters are displayed
one at a time on the monitor. This is called echo, and happens
in many of the applications you run.
With the above paragraph in mind, it should be clear that when
you are typing a line of data into SIMPLEIO, the characters are
being echoed by DOS, and when you return the carriage by hitting
return or enter, the characters are given to the program. As
each character is given to the program, it displays it on the
screen resulting in a repeat of the line typed in. To better
illustrate this, type a line with a capital X somewhere in the
middle of the line. You can type as many characters as you like
following the X and they will all display because the characters
are being read in under DOS, echoed to the monitor, and placed
in the DOS input buffer. DOS doesn't think there is anything
special about a capital X. When the string is given to the
program, however, the characters are accepted by the program one
at a time and sent to the monitor one at a time, until a capital
X is encountered. After the capital X is displayed, the loop is
terminated, and the program is terminated. The characters on the
Page 9-3
Chapter 9 - Standard Input/Output
input line following the capital X are not displayed because the
capital X signalled program termination.
Compile and run SIMPLEIO.C. After running the program several
times and feeling confident that you understand the above
explanation, we will go on to another program.
Don't get discouraged by the above seemingly weird behavior of
the I/O system. It is strange, but there are other ways to get
data into the computer. You will actually find the above method
useful for many applications, and you will probably find some of
the following useful also.
ANOTHER STRANGE I/O METHOD
-----------------------------------------------------------------
Load the file named SINGLEIO.C and display it ================
on your monitor for another method of character SINGLEIO.C
I/O. Once again, we start with the standard ================
I/O header file using the "<" and ">" method of
defining it. Then we define a variable named c, and we print a
welcoming message. Like the last program, we are in a loop that
will continue to execute until we type a capital X, but the
action is a little different here.
The function named _getch() is a get character function. It
differs from the function named getchar() in that it does not get
tied up in DOS. It reads the character in without echo, and puts
it directly into the program where it is operated on immediately.
This function therefore reads a character, immediately displays
it on the screen, and continues the operation until a capital X
is typed. Note that although _getch() is available with most
popular microcomputer C compilers, it is not included in the ANSI
standard and may not be available with all C compilers. It's use
may therefore make a program nonportable. If your compiler does
not support the _getch() function, use the getchar() function
instead.
When you compile and run this program, you will find that there
is no repeat of the lines when you hit a carriage return, and
when you hit the capital X, the program terminates immediately.
No carriage return is needed to get it to accept the line with
the X in it, so this program operates a little differently from
the last one. However, we do have another problem here, since
there is no linefeed with the carriage return.
NOW WE NEED A LINE FEED
-----------------------------------------------------------------
It is not apparent to you in most application ================
programs but when you hit the enter key, the BETTERIN.C
program supplies a linefeed to go with the ================
carriage return. You need to return to the
Page 9-4
Chapter 9 - Standard Input/Output
left side of the monitor and you also need to drop down a line.
The linefeed is not automatic. We need to improve our program to
do this also. If you will load and display the program named
BETTERIN.C, you will find a change to incorporate this feature.
In BETTERIN.C, we have two additional statements at the beginning
that will define the character codes for the linefeed (LF), and
the carriage return (CR). If you look at any ASCII table you
will find that the codes 10 and 13 are exactly as defined here.
In the main program, after outputting the character in line 15,
we compare it to CR, and if it is equal to CR, we also output a
linefeed which is the LF. We could have completely omitted the
two #define statements and used the statement
if (c == 13) putchar(10); but it would not be very descriptive of
what we are doing here. The method used in this program
represents better programming practice.
You will notice that line 16 deviates from the usual style for an
if statement, but we have a choice. We can format the code
anyway we desire to improve the readability. It is strictly a
programmer's choice.
Compile and run BETTERIN.C to see if it does what we have said it
should do. It should display exactly what you type in, including
a linefeed with each carriage return, and should stop immediately
when you type a capital X. Once again, if your compiler does not
support _getch(), use the getchar() function.
WHICH METHOD IS BEST?
-----------------------------------------------------------------
We have examined two methods of reading characters into a C
program, and are faced with a choice of which one we should use.
It really depends on the application because each method has
advantages and disadvantages.
When using the first method, DOS is actually doing all of the
work for us by storing the characters in an input buffer and
signaling us when a full line has been entered. We could write a
program that, for example, did a lot of calculations, then went
to get some input. While we were doing the calculations, DOS
would be accumulating a line of characters for us, and they would
be there when we were ready for them. However, we could not read
in single keystrokes because DOS would not report a buffer of
characters to us until it recognized a carriage return.
The second method, used in BETTERIN.C, allows us to get a single
character, and act on it immediately. We do not have to wait
until DOS decides we can have a line of characters. We cannot do
anything else while we are waiting for a character because we are
waiting for the input keystroke and tying up the entire machine.
This method is useful for highly interactive types of program
Page 9-5
Chapter 9 - Standard Input/Output
interfaces. It is up to you as the programmer to decide which is
best for your needs.
I should mention at this point that there is also an _ungetch()
function that works with the _getch() function. If you _getch()
a character and find that you have gone one too far, you can
_ungetch() it back to the input device. This simplifies some
programs because you don't know that you don't want the character
until you get it. You can only _ungetch() one character back to
the input device, but that is sufficient to accomplish the task
this function was designed for. It is difficult to demonstrate
this function in a simple program so its use will be up to you
to study when you need it. Another function that may be
available with your compiler, but is not part of the ANSI
standard, is the _getche() function which is identical to the
_getch() function except that it echoes the character to the
monitor for you.
The discussion so far in this chapter should be a good indication
that, while the C programming language is very flexible, it does
put a lot of responsibility on you as the programmer to keep many
details in mind.
NOW TO READ IN SOME INTEGERS
-----------------------------------------------------------------
Load and display the file named INTIN.C for an =================
example of reading some formatted data from INTIN.C
the keyboard. The structure of this program =================
is very similar to the last three except that
we define an int type variable and loop until the variable
somehow acquires the value of 100. Instead of reading in a
character at a time, as we have in the last three example
programs, we read in an entire integer value with one call using
the function named scanf(). This function is very similar to the
printf() that you have been using for quite some time by now
except that it is used for input instead of output. Examine the
line with the scanf() and you will notice that it does not ask
for the variable valin directly, but gives the address of the
variable since it expects to have a value returned from the
function. Recall that a function must have the address of a
variable in order to return a value to that variable in the
calling program. Failing to supply a pointer to the parameter in
the scanf() function is the most common problem encountered in
using this function.
The function scanf() scans the input line until it finds the
first data field. It ignores leading blanks and in this case,
it reads integer characters until it finds a blank or an invalid
decimal character, at which time it stops reading and returns
the value.
Page 9-6
Chapter 9 - Standard Input/Output
Remembering our discussion above about the way the DOS input
buffer works, it should be clear that nothing is actually acted
on until a complete line is entered and it is terminated by a
carriage return. At this time, the buffer is input, and our
program will search across the line reading all integer values
it can find until the line is completely scanned. This is
because we are in a loop and we tell it to find a value, print
it, find another, print it, etc. If you enter several values on
one line, it will read each one in succession and display the
values. Entering the value of 100 will cause the program to
terminate, and entering the value 100 with other values
following, will cause termination before the following values
are considered.
IT MAKES WRONG ANSWERS SOMETIMES
-----------------------------------------------------------------
If you enter a number up to and including 32767, it will display
correctly, but if you enter a larger number, it will appear to
make an error unless your system uses a much larger range for an
int type variable. For example, if you enter the value 32768, it
will display the value of -32768, entering the value 65536 will
display as a zero. These are not errors but are caused by the
way an int variable is defined. The most significant bit of the
16 bit pattern available for the integer variable is the sign
bit, so there are only 15 bits left for the value. The variable
can therefore only have the values from -32768 to 32767, any
other values are outside the range of integer variables. This is
up to you to take care of in your programs. It is another
example of the increased responsibility you must assume using C
rather than another high level language such as Pascal, Modula-2,
etc.
The above paragraph is true for most MS-DOS C compilers. There
is a very small possibility that your compiler uses an integer
value stored in a field size other than 16 bits. If that is the
case, the same principles will be true but with different limits
than those given above.
Compile and run this program, entering several numbers on a line
to see the results, and with varying numbers of blanks between
the numbers. Try entering numbers that are too big to see what
happens, and finally enter some invalid characters to see what
the system does with nondecimal characters.
CHARACTER STRING INPUT
-----------------------------------------------------------------
Load and display the file named STRINGIN.C for ================
an example of reading a string variable from STRINGIN.C
the keyboard. This program is identical to the ================
last one except that instead of an integer
variable, we have defined a string variable with an upper limit
Page 9-7
Chapter 9 - Standard Input/Output
of 24 characters (remember that a string variable must have a
null character at the end). The variable in the scanf() does not
need an & because big is an array variable and by definition it
is already a pointer. This program should require no additional
explanation. Compile and run it to see if it works the way you
expect.
You probably got a surprise when you ran it because it separated
your sentence into separate words. When used in the string mode
of input, scanf() reads characters into the string until it comes
to either the end of a line or a blank character. Therefore, it
reads a word, finds the blank following it, and displays the
result. Since we are in a loop, this program continues to read
words until it exhausts the DOS input buffer. We have written
this program to stop whenever it finds a capital X in column 1,
but since the sentence is split up into individual words, it will
stop anytime a word begins with capital X. Try entering a 5 word
sentence with a capital X as the first character in the third
word. You should get the first three words displayed, and the
last two simply ignored when the program stops.
Try entering more than 24 characters to see what the program
does. In an actual program, it is your responsibility to count
characters and stop when the input buffer is full. You may be
getting the feeling that a lot of responsibility is placed on you
when writing in C. Along with this responsibility you get a lot
of flexibility in the bargain also.
INPUT/OUTPUT PROGRAMMING IN C
-----------------------------------------------------------------
C was not designed to be used as a language for lots of input and
output, but as a systems language where a lot of internal
operations are required. You would do well to use another
language for I/O intensive programming, but C could be used if
you desire. The keyboard input is very flexible, allowing you to
get at the data in a very low level way, but very little help is
given you. It is therefore up to you to take care of all of the
bookkeeping chores associated with your required I/O operations.
This may seem like a real pain in the neck, but in any given
program, you only need to define your input routines once and
then use them as needed. Don't let this worry you. As you gain
experience with C, you will easily handle your I/O requirements.
One final point must be made about these I/O functions. It is
perfectly permissible to intermix scanf() and getchar() functions
during read operations. In the same manner, it is also fine to
intermix the output functions, printf() and putchar() in any way
you desire.
Page 9-8
Chapter 9 - Standard Input/Output
IN MEMORY I/O
-----------------------------------------------------------------
The next operation may seem a little strange =================
at first, but you will probably see lots of INMEM.C
uses for it as you gain experience. Load the =================
file named INMEM.C and display it for another
type of I/O, one that never accesses the outside world, but stays
in the computer. In INMEM.C, we define a few variables, then
assign some values to the ones named numbers for illustrative
purposes and then use an sprintf() function. The function acts
just like a normal printf() function except that instead of
printing the line of output to a device, it prints the line of
formatted output to a character string in memory. In this case
the string goes to the string variable named line, because that
is the string name we inserted as the first argument in the
sprintf() function. The spaces after the 2nd %d were put there to
illustrate that the next function will search properly across the
line. We print the resulting string and find that the output is
identical to what it would have been by using a printf() instead
of the sprintf() in the first place. You will see that when you
compile and run the program shortly.
Since the generated string is still in memory, we can now read it
with the function sscanf(). We tell the function in its first
argument that line is the string to use for its input, and the
remaining parts of the line are exactly what we would use if we
were going to use the scanf() function and read data from outside
the computer. Note that it is essential that we use pointers to
the data because we want to return data from a function. Just to
illustrate that there are many ways to declare a pointer several
methods are used, but all are ultimately pointers. The first two
simply declare the address of the elements of the array, while
the last three use the fact that result, without the accompanying
subscript, is a pointer. Just to keep it interesting, the values
are read back in reverse order. Finally the values are displayed
on the monitor.
IS THAT REALLY USEFUL?
-----------------------------------------------------------------
It seems sort of silly to read input data from within the
computer but it does have a real purpose. It is possible to read
data from an input device using any of the standard functions and
then do a format conversion in memory. You could read in a line
of data, look at a few significant characters, then use these
formatted input routines to reduce the line of data to internal
representation. That would sure beat writing your own data
formatting routines.
Page 9-9
Chapter 9 - Standard Input/Output
STANDARD ERROR OUTPUT
-----------------------------------------------------------------
Sometimes it is desirable to redirect the =================
output from the standard output device to a SPECIAL.C
file. However, you may still want the error =================
messages to go to the standard output device,
in our case the monitor. This next function allows you to do
that. Load and display SPECIAL.C for an example of this new
function. The program consists of a loop with two messages
output, one to the standard output device and the other to the
standard error device. The message to the standard error device
is output with the function fprintf() and includes the device
name stderr as the first argument. Other than those two small
changes, it is the same as our standard printf() function. (You
will see more of the fprintf() function in the next chapter, but
its operation fit in better as a part of this chapter.) Ignore
the line with the exit for the moment, we will return to it.
Compile and run this program, and you will find 12 lines of
output on the monitor. To see the difference, run the program
again with redirected output to a file named STUFF by entering
the following line at the DOS prompt;
C> special >stuff
This time you will only get the 6 lines output to the standard
error device, and if you look in your directory, you will find
that the file named STUFF contains the other 6 lines, those to
the standard output device. You can use I/O redirection with any
of the programs we have run so far, and as you may guess, you can
also read from a file using I/O redirection but we will study a
better way to read from a file in the next chapter. More
information about I/O redirection can be found in your DOS
manual.
WHAT ABOUT THE exit(4) STATEMENT?
-----------------------------------------------------------------
Now to keep our promise about the exit(4) statement. Redisplay
the file named SPECIAL.C on your monitor. The last statement
exits the program and returns the value of 4 to DOS. Any number
from 0 to 19 can be used in the parentheses for DOS
communication. If you are operating in a BATCH file, this number
can be tested with the ERRORLEVEL command.
Most compilers that operate in several passes return a 1 with
this mechanism to indicate that a fatal error has been detected
and it would be a waste of time to go on to another pass
resulting in even more errors.
It is therefore wise to use a batch file for compiling programs
and testing the returned value for errors. A check of the
documentation for my computer, resulted in a minimal and
Page 9-10
Chapter 9 - Standard Input/Output
confusing documentation of the ERRORLEVEL command, so a brief
description of it is given in this file in case your
documentation does not include enough information to allow you to
use it.
One additional feature must be mentioned here. Since we wish to
return an int value to the operating system, we must define the
main program entry point as returning an int rather than a void
as we have used in most of the example programs to this point.
Refer to line 5 for an example of this extension.
PROGRAMMING EXERCISES
-----------------------------------------------------------------
1. Write a program to read in a character using a loop, and
display the character in its normal char form. Also display
it as a decimal number. Check for a dollar sign to use as the
stop character. Use the _getch() form of input so it will
print immediately. Hit some of the special keys, such as
function keys, when you run the program for some surprises.
You will get two inputs from the special keys, the first
being a zero which is the indication to the system that a
special key was hit.
2. Add a character string to SINGLEIO.C and store the input
characters in the string. When the X is detected, add a
terminating null to the string and print out the string
with a printf() function call.
Page 9-11